using System.Data;
using System.Reflection;
using System.Text.RegularExpressions;

namespace ThingsGateway.SqlSugar
{
    /// <summary>
    /// TDengine 代码优先(CodeFirst)实现
    /// </summary>
    public class TDengineCodeFirst : CodeFirstProvider
    {
        /// <summary>
        /// 执行代码优先逻辑
        /// </summary>
        /// <param name="entityType">实体类型</param>
        /// <param name="entityInfo">实体信息</param>
        protected override void Execute(Type entityType, EntityInfo entityInfo)
        {
            var attr = GetCommonSTableAttribute(entityInfo.Type.GetCustomAttribute<STableAttribute>());
            if (attr?.STableName != null && attr?.Tag1 != null)
            {
                entityInfo.DbTableName = attr.STableName;
                Context.MappingTables.Add(entityInfo.EntityName, entityInfo.DbTableName);
            }
            //var entityInfo = this.Context.EntityMaintenance.GetEntityInfoNoCache(entityType);
            if (entityInfo.Discrimator.HasValue())
            {
                if (!Regex.IsMatch(entityInfo.Discrimator, @"^(?:\w+:\w+)(?:,\w+:\w+)*$")) { throw new SqlSugarLangException("The format should be type:cat for this type, and if there are multiple, it can be FieldName:cat,FieldName2:dog ", "格式错误应该是type:cat这种格式，如果是多个可以FieldName:cat,FieldName2:dog，不要有空格"); }
                var array = entityInfo.Discrimator.Split(',');
                foreach (var disItem in array)
                {
                    var name = disItem.Split(':').First();
                    var value = disItem.Split(':').Last();
                    entityInfo.Columns.Add(new EntityColumnInfo() { PropertyInfo = typeof(DiscriminatorObject).GetProperty(nameof(DiscriminatorObject.FieldName)), IsOnlyIgnoreUpdate = true, DbColumnName = name, UnderType = typeof(string), PropertyName = name, Length = 50 });
                }
            }
            if (this.MappingTables.TryGetValue(entityType, out string? v))
            {
                entityInfo.DbTableName = v;
                this.Context.MappingTables.Add(entityInfo.EntityName, entityInfo.DbTableName);
            }
            if (this.DefaultLength > 0)
            {
                foreach (var item in entityInfo.Columns)
                {
                    if (item.PropertyInfo.PropertyType == UtilConstants.StringType && item.DataType.IsNullOrEmpty() && item.Length == 0)
                    {
                        item.Length = DefaultLength;
                    }
                    if (item.DataType?.Contains(',') == true && !Regex.IsMatch(item.DataType, @"\d\,\d"))
                    {
                        var types = item.DataType.Split(',').ToHashSet(StringComparer.OrdinalIgnoreCase);
                        var mapingTypes = this.Context.Ado.DbBind.MappingTypes.Select(it => it.Key).ToHashSet(StringComparer.OrdinalIgnoreCase);
                        var mappingType = types.FirstOrDefault(it => mapingTypes.Contains(it)).ToLower();
                        if (mappingType != null)
                        {
                            item.DataType = mappingType;
                        }
                        if (item.DataType == "varcharmax")
                        {
                            item.DataType = "nvarchar(max)";
                        }
                    }
                }
            }
            var tableName = GetTableName(entityInfo);
            this.Context.MappingTables.Add(entityInfo.EntityName, tableName);
            entityInfo.DbTableName = tableName;
            entityInfo.Columns.ForEach(it =>
            {
                it.DbTableName = tableName;
                if (it.UnderType?.Name == "DateOnly" && it.DataType == null)
                {
                    it.DataType = "Date";
                }
                if (it.UnderType?.Name == "TimeOnly" && it.DataType == null)
                {
                    it.DataType = "Time";
                }
            });
            var isAny = this.Context.DbMaintenance.IsAnyTable(tableName, false);
            if (isAny && entityInfo.IsDisabledUpdateAll)
            {
                return;
            }
            if (isAny)
                ExistLogic(entityInfo);
            else
                NoExistLogic(entityInfo);

            this.Context.DbMaintenance.AddRemark(entityInfo);
            //this.Context.DbMaintenance.AddIndex(entityInfo);
            //base.CreateIndex(entityInfo);
            this.Context.DbMaintenance.AddDefaultValue(entityInfo);

            STable.Tags = null;
        }

        /// <summary>
        /// 表存在时的处理逻辑
        /// </summary>
        /// <param name="entityInfo">实体信息</param>
        public override void ExistLogic(EntityInfo entityInfo)
        {
            if (entityInfo.Columns.HasValue() && entityInfo.IsDisabledUpdateAll == false)
            {
                //if(entityInfo.Columns.Where(it => it.IsPrimarykey).Count() > 1){throw new SqlSugarException("Multiple primary keys do not support modifications");}

                var tableName = GetTableName(entityInfo);
                var dbColumns = this.Context.DbMaintenance.GetColumnInfosByTableName(tableName, false);
                ConvertColumns(dbColumns);
                var attr = GetCommonSTableAttribute(entityInfo.Type.GetCustomAttribute<STableAttribute>());
                var entityColumns = entityInfo.Columns.Where(it => it.IsIgnore == false).ToList();
                if (attr?.Tag1 != null)
                {
                    entityColumns = entityInfo.Columns.Where(it => it.IsIgnore == false
                    || it.DbColumnName?.EqualCase(attr.Tag1) == true
                     || it.DbColumnName?.EqualCase(attr.Tag2) == true
                      || it.DbColumnName?.EqualCase(attr.Tag3) == true
                       || it.DbColumnName?.EqualCase(attr.Tag4) == true
                  ).ToList();
                    foreach (var item in entityColumns)
                    {
                        if (item.DbColumnName == null)
                        {
                            item.DbColumnName = item.PropertyName;
                        }
                    }
                }
                var dropColumns = dbColumns
                                          .Where(dc => !entityColumns.Any(ec => dc.DbColumnName.Equals(ec.OldDbColumnName, StringComparison.CurrentCultureIgnoreCase)) && !entityColumns.Any(ec => dc.DbColumnName.Equals(ec.DbColumnName, StringComparison.CurrentCultureIgnoreCase)))
                                          ;
                var addColumns = entityColumns
                                          .Where(ec => (ec.OldDbColumnName.IsNullOrEmpty() || !dbColumns.Any(dc => dc.DbColumnName.Equals(ec.OldDbColumnName, StringComparison.CurrentCultureIgnoreCase))) && !dbColumns.Any(dc => ec.DbColumnName.Equals(dc.DbColumnName, StringComparison.CurrentCultureIgnoreCase)));

                var renameColumns = entityColumns
                    .Where(it => !string.IsNullOrEmpty(it.OldDbColumnName))
                    .Where(entityColumn => dbColumns.Any(dbColumn => entityColumn.OldDbColumnName.Equals(dbColumn.DbColumnName, StringComparison.CurrentCultureIgnoreCase)))
                    ;

                var isMultiplePrimaryKey = dbColumns.Where(it => it.IsPrimarykey).Count() > 1 || entityColumns.Where(it => it.IsPrimarykey).Count() > 1;

                var isChange = false;
                foreach (var item in addColumns)
                {
                    this.Context.DbMaintenance.AddColumn(tableName, EntityColumnToDbColumn(entityInfo, tableName, item));
                    isChange = true;
                }
                if (entityInfo.IsDisabledDelete == false)
                {
                    foreach (var item in dropColumns)
                    {
                        this.Context.DbMaintenance.DropColumn(tableName, item.DbColumnName);
                        isChange = true;
                    }
                }

                foreach (var item in renameColumns)
                {
                    this.Context.DbMaintenance.RenameColumn(tableName, item.OldDbColumnName, item.DbColumnName);
                    isChange = true;
                }

                if (isChange && IsBackupTable)
                {
                    this.Context.DbMaintenance.BackupTable(tableName, tableName + DateTime.Now.ToString("yyyyMMddHHmmss"), MaxBackupDataRows);
                }
                ExistLogicEnd(entityColumns);
            }
        }

        /// <summary>
        /// 表不存在时的处理逻辑
        /// </summary>
        /// <param name="entityInfo">实体信息</param>
        public override void NoExistLogic(EntityInfo entityInfo)
        {
            List<DbColumnInfo> dbColumns = new List<DbColumnInfo>();
            foreach (var item in entityInfo.Columns.Where(it => it.IsIgnore != true && it.PropertyName != "TagsTypeId").OrderBy(it => it.UnderType == typeof(DateTime) ? 0 : 1))
            {
                var addItem = EntityColumnToDbColumn(entityInfo, entityInfo.DbTableName, item);
                dbColumns.Add(addItem);
            }
            var attr = GetCommonSTableAttribute(entityInfo.Type.GetCustomAttribute<STableAttribute>());
            var oldTableName = entityInfo.DbTableName;
            if (attr != null)
            {
                entityInfo.DbTableName += ("{stable}" + this.Context.Utilities.SerializeObject(attr));
            }
            if (attr?.Tag1 != null)
            {
                dbColumns = dbColumns.Where(it =>
                 it.DbColumnName?.EqualCase(attr.Tag1) == true
                   && it.DbColumnName?.EqualCase(attr.Tag2) == true
                   && it.DbColumnName?.EqualCase(attr.Tag3) == true
                   && it.DbColumnName?.EqualCase(attr.Tag4) == true
                ).ToList();
            }
            var dbMain = (TDengineDbMaintenance)this.Context.DbMaintenance;
            dbMain.EntityInfo = entityInfo;
            dbMain.CreateTable(entityInfo.DbTableName, dbColumns);
            entityInfo.DbTableName = oldTableName;
        }

        /// <summary>
        /// 将实体列信息转换为数据库列信息
        /// </summary>
        /// <param name="entityInfo">实体信息</param>
        /// <param name="tableName">表名</param>
        /// <param name="item">实体列信息</param>
        /// <returns>数据库列信息</returns>
        protected override DbColumnInfo EntityColumnToDbColumn(EntityInfo entityInfo, string tableName, EntityColumnInfo item)
        {
            DbColumnInfo result = new DbColumnInfo() { Length = item.Length, DecimalDigits = item.DecimalDigits, Scale = item.DecimalDigits, TableName = tableName, DbColumnName = item.DbColumnName, DataType = item.DataType };
            if (result.DataType.IsNullOrEmpty())
            {
                result.DataType = GetDatabaseTypeName(item.UnderType.Name);
            }
            return result;
        }

        /// <summary>
        /// 获取通用STable特性
        /// </summary>
        /// <param name="sTableAttribute">STable特性</param>
        /// <returns>处理后的STable特性</returns>
        private STableAttribute GetCommonSTableAttribute(STableAttribute sTableAttribute)
        {
            return TaosUtilMethods.GetCommonSTableAttribute(this.Context, sTableAttribute);
        }

        /// <summary>
        /// 根据类型名称获取数据库类型名称
        /// </summary>
        /// <param name="typeName">类型名称</param>
        /// <returns>数据库类型名称</returns>
        public string GetDatabaseTypeName(string typeName)
        {
            switch (typeName.ToLower())
            {
                case "bool":
                    return "BOOL";
                case "datetime":
                    return "TIMESTAMP";
                case "boolean":
                    return "BOOL";
                case "byte":
                    return "TINYINT UNSIGNED";
                case "sbyte":
                    return "TINYINT";
                case "char":
                    return "NCHAR";
                case "decimal":
                    return "FLOAT";
                case "double":
                    return "DOUBLE";
                case "float":
                case "single":
                    return "FLOAT";
                case "int":
                    return "INT";
                case "int32":
                    return "INT";
                case "int16":
                    return "INT";
                case "int64":
                    return "BIGINT";
                case "uint":
                case "uint32":
                    return "INT UNSIGNED";
                case "long":
                    return "BIGINT";
                case "ulong":
                case "uint64":
                    return "BIGINT UNSIGNED";
                case "short":
                    return "SMALLINT";
                case "ushort":
                case "uint16":
                    return "SMALLINT UNSIGNED";
                case "string":
                    return "VARCHAR";
                // 添加其他类型的映射关系

                default:
                    return "VARCHAR"; // 如果未识别到类型，则返回原始类型名称
            }
        }
    }
}